﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Net;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Media;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
using System.Windows.Threading;

using WinForms = System.Windows.Forms;

using Microsoft.Win32;

using MiniTwitter.Extensions;
using MiniTwitter.Input;
using MiniTwitter.Net;
using MiniTwitter.Net.Twitter;
using MiniTwitter.Properties;
using MiniTwitter.Themes;

namespace MiniTwitter
{
    /// <summary>
    /// MainWindow.xaml の相互作用ロジック
    /// </summary>
    public partial class MainWindow : Window, INotifyPropertyChanged {
		#region added by yuki.
		private bool _tmpOffPopup = false;
		public bool TmpOffPopup {
			get { return _tmpOffPopup; }
			set {
				if (_tmpOffPopup != value) {
					_tmpOffPopup = value;
					OnPropertyChanged("TmpOffPopup");
				}
			}
		}

		private bool isShowTwitForm;
		public bool IsShowTwitForm {
			get { return isShowTwitForm; }
			set {
				if (isShowTwitForm != value) {
					isShowTwitForm = value;
					OnPropertyChanged("IsShowTwitForm");
				}
			}
		}

		#endregion

		public MainWindow()
        {
            InitializeComponent();
			this.DataContext = this;

		}

        private TwitterClient client = new TwitterClient();

        private bool _isClosing = false;

        private long? in_reply_to_status_id = null;

        private readonly PopupWindow popupWindow = new PopupWindow();
        private readonly WinForms.NotifyIcon notifyIcon = new WinForms.NotifyIcon();

        private readonly DispatcherTimer refreshTimer = new DispatcherTimer(DispatcherPriority.Background);
        private readonly DispatcherTimer refreshReplyTimer = new DispatcherTimer(DispatcherPriority.Background);
        private readonly DispatcherTimer refreshMessageTimer = new DispatcherTimer(DispatcherPriority.Background);

        private readonly DispatcherTimer quickSearchTimer = new DispatcherTimer(DispatcherPriority.Background);

		#region added by yuki.
		private readonly DispatcherTimer refreshSearchResultTimer = new DispatcherTimer(DispatcherPriority.Background);
		#endregion

        private readonly ObservableCollection<Timeline> timelines = new ObservableCollection<Timeline>();

        public ObservableCollection<Timeline> Timelines
        {
            get { return timelines; }
        }

        public string StatusText
        {
            get { return (string)GetValue(StatusTextProperty); }
            set { SetValue(StatusTextProperty, value); }
        }

        public static readonly DependencyProperty StatusTextProperty =
                DependencyProperty.Register("StatusText", typeof(string), typeof(MainWindow), new PropertyMetadata(App.NAME + " " + App.VERSION));

        public enum RefreshTarget
        {
            All,
            Recent,
            Replies,
            Archive,
            Message,
			#region added by yuki.
			Search,
			#endregion
		}

        private void InitializeTimeline()
        {
            // 初期タイムラインを作成
            Timelines.Add(new Timeline { Type = TimelineType.Recent, Name = "Recent" });
            Timelines.Add(new Timeline { Type = TimelineType.Replies, Name = "Replies" });
            Timelines.Add(new Timeline { Type = TimelineType.Archive, Name = "Archive" });
            Timelines.Add(new Timeline { Type = TimelineType.Message, Name = "Message" });

			#region Added by yuki.
			if (Properties.Settings.Default.IsEnableSearchTimeline) {
				Timelines.Add(new Timeline { Type = TimelineType.Search, Name = "Search" });
			}
			#endregion

			// ユーザータイムラインを作成
            foreach (var item in Settings.Default.Timelines)
            {
                Timelines.Add(item);
            }
            // タイムラインをソート
            Timelines.Sort(Settings.Default.SortCategory, Settings.Default.SortDirection);
            popupWindow.Timeline.Sort(Settings.Default.SortCategory, Settings.Default.SortDirection);

		}

        private void InitializeFilter()
        {
            var replies = Timelines.TypeAt(TimelineType.Replies);
            replies.Filters.Clear();
            replies.Filters.Add(new Filter { Type = FilterType.RegexText, Pattern = string.Format(@"@{0}[^a-zA-Z_0-9]", client.LoginedUser.ScreenName) });
            var archive = Timelines.TypeAt(TimelineType.Archive);
            archive.Filters.Clear();
            archive.Filters.Add(new Filter { Type = FilterType.Name, Pattern = Settings.Default.Username });
        }

        private void InitializeAutoRefresh()
        {
            refreshTimer.IsEnabled = Settings.Default.EnableAutoRefresh;
            refreshTimer.Interval = TimeSpan.FromMinutes(Settings.Default.RefreshTick);
            refreshReplyTimer.IsEnabled = Settings.Default.EnableAutoRefresh;
            refreshReplyTimer.Interval = TimeSpan.FromMinutes(Settings.Default.RefreshReplyTick);
            refreshMessageTimer.IsEnabled = Settings.Default.EnableAutoRefresh;
            refreshMessageTimer.Interval = TimeSpan.FromMinutes(Settings.Default.RefreshMessageTick);

			#region added by yuki.
			if (Properties.Settings.Default.IsEnableSearchTimeline) {
				refreshSearchResultTimer.IsEnabled = Settings.Default.IsEnableSearchTimeline;
				refreshSearchResultTimer.Interval = TimeSpan.FromMinutes(10); //T.B.D とりあえず10分にしておく
			}
			#endregion
		}

        private void InitializeTwitter()
        {
            client.ConvertShortUrl = true;
            if (Settings.Default.UseProxy)
            {
                if (Settings.Default.UseIEProxy)
                {
                    client.Proxy = WebRequest.GetSystemWebProxy();
                }
                else
                {
                    IWebProxy proxy;
                    if (Settings.Default.ProxyPortNumber.IsNullOrEmpty())
                    {
                        proxy = new WebProxy(Settings.Default.ProxyAddress);
                    }
                    else
                    {
                        proxy = new WebProxy(Settings.Default.ProxyAddress + ":" + Settings.Default.ProxyPortNumber);
                    }
                    if (!Settings.Default.ProxyUsername.IsNullOrEmpty() && !Settings.Default.ProxyPassword.IsNullOrEmpty())
                    {
                        proxy.Credentials = new NetworkCredential(Settings.Default.ProxyUsername, Settings.Default.ProxyPassword);
                    }
                    client.Proxy = proxy;
                }
            }
            client.Footer = Settings.Default.EnableTweetFooter ? Settings.Default.TweetFooter : string.Empty;
        }

        private void InitializePopupWindow()
        {
            popupWindow.Location = Settings.Default.PopupLocation;
            popupWindow.CloseTick = Settings.Default.PopupCloseTick;
        }

        /// <summary>
        /// キーボードショートカットを初期化、登録する
        /// </summary>
        private void InitializeKeyboardShortcut()
        {
            if (Settings.Default.KeyMapping == null)
            {
                if (KeyMapping.KeyMappings.Count == 0)
                {
                    return;
                }
                var keyMapping = KeyMapping.GetKeyMapping(0);
                Settings.Default.KeyMapping = keyMapping.Name;
                Settings.Default.KeyBindings.Clear();
                foreach (var item in keyMapping.KeyBindings)
                {
                    Settings.Default.KeyBindings.Add(item);
                }
            }
            InputBindings.Clear();
            TweetTextBox.InputBindings.Clear();
            TimelineTabControl.InputBindings.Clear();
            foreach (var keyBinding in Settings.Default.KeyBindings)
            {
                var inputBinding = new InputBinding(keyBinding.Action.ToCommand(), new KeyGesture(keyBinding.Key, keyBinding.ModifierKeys));
                switch (keyBinding.ActionSpot)
                {
                    case KeyActionSpot.All:
                        InputBindings.Add(inputBinding);
                        break;
                    case KeyActionSpot.TweetTextBox:
                        TweetTextBox.InputBindings.Add(inputBinding);
                        break;
                    case KeyActionSpot.Timeline:
                        TimelineTabControl.InputBindings.Add(inputBinding);
                        break;
                    case KeyActionSpot.Global:
                        break;
                }
            }
        }

        private void Login()
        {
            // ログインを開始
            this.AsyncInvoke(() => StatusText = "ログインしています...");
            ThreadPool.QueueUserWorkItem(LoginCallback);
        }

        private void LoginCallback(object state)
        {
            // 設定に従いログイン開始
            var result = client.Login(Settings.Default.Username, Settings.Default.Password);
            if (result == null)
            {
                // ユーザーIDとパスワードが空欄
                return;
            }
            if (!result.Value)
            {
                // ログインに失敗
                this.Invoke(() => StatusText = "ログインに失敗しました");
            }
            else
            {
                // すべてのタイムラインの項目を削除する
                Timelines.ClearAll();
                // フィルタを初期化
                InitializeFilter();
                // すべてのタイムラインを取得する
                RefreshTimeline(RefreshTarget.All);

				#region added by yuki.
				// 検索を取得する
				if (Properties.Settings.Default.IsEnableSearchTimeline) {
					RefreshTimeline(RefreshTarget.Search);
				}
				#endregion

				// 自動更新タイマーを初期化
                InitializeAutoRefresh();
            }
        }

        private void SetStatusMessage(bool isSuccess)
        {
            this.Invoke(p => StatusText = p, isSuccess ? DateTime.Now.ToString("G") + " に取得完了" : "タイムラインの取得に失敗しました");
        }

        private void RefreshTimeline(RefreshTarget target)
        {
            // タイムラインを取得
            this.AsyncInvoke(() => StatusText = "タイムラインを取得しています...");
            Status[] statuses;
            switch (target)
            {
                case RefreshTarget.All:
                    // Recent を取得する
                    statuses = client.RecentTimeline;
                    // 取得できたか確認する
                    if (statuses == null)
                    {
                        SetStatusMessage(false);
                        return;
                    }
                    if (Settings.Default.IgnoreRegex != null)
                    {
                        statuses = statuses.Where(p => !Settings.Default.IgnoreRegex.IsMatch(p.Text)).ToArray();
                    }
                    // ステータスを反映させる
                    Timelines.Update(statuses);
                    // 返信タイムラインを反映させる
                    statuses = client.RepliesTimeline;
                    if (Settings.Default.IgnoreRegex != null)
                    {
                        statuses = statuses.Where(p => !Settings.Default.IgnoreRegex.IsMatch(p.Text)).ToArray();
                    }
                    Timelines.Update(TimelineType.Replies, statuses);
                    Timelines.Update(TimelineType.User, statuses);
                    // アーカイブを反映させる
                    statuses = client.ArchiveTimeline;
                    if (Settings.Default.IgnoreRegex != null)
                    {
                        statuses = statuses.Where(p => !Settings.Default.IgnoreRegex.IsMatch(p.Text)).ToArray();
                    }
                    Timelines.Update(TimelineType.Archive, statuses);
                    Timelines.Update(TimelineType.User, statuses);
					// メッセージを受信
                    Timelines.Update(TimelineType.Message, client.ReceivedMessages);
                    Timelines.Update(TimelineType.Message, client.SentMessages);
                    // 取得完了
                    SetStatusMessage(true);
                    return;
                case RefreshTarget.Recent:
                    statuses = client.RecentTimeline;
                    break;
                case RefreshTarget.Replies:
                    statuses = client.RepliesTimeline;
                    break;
                case RefreshTarget.Archive:
                    Timelines.Update(client.ArchiveTimeline);
                    return;
                case RefreshTarget.Message:
                    var messages = client.ReceivedMessages;
                    if (messages == null)
                    {
                        return;
                    }
                    var normalized = Timelines.Normalize(messages);
                    if (normalized.Length > 0)
                    {
						//modified by yuki. add TmpOffPopup Condition
                        if (Settings.Default.EnablePopup && !TmpOffPopup)
                        {
                            popupWindow.Show(normalized);
                        }
                        var sound = Settings.Default.SoundBindings.Where(p => p.IsEnabled && p.Action == SoundAction.Message).FirstOrDefault();
                        if (sound != null)
                        {
                            new SoundPlayer(sound.FileName).Play();
                        }
                    }
                    Timelines.Update(TimelineType.Message, messages);
                    return;
				#region editted by yuki.
				case RefreshTarget.Search:
					var result = client.SearchResult;
					Timelines.Update(TimelineType.Search, result);
					var newItems = Timelines.Normalize(TimelineType.Search, result);
					if (newItems.Length > 0 && Settings.Default.EnablePopup && !TmpOffPopup) {
						if (!Settings.Default.PopupOnlyNotActive || this.Invoke<bool>(() => !IsActive)) {
							popupWindow.Show(newItems);
						}
						var action = SoundAction.Status; //T.B.D とりあえず新しいStatusと同じ音を鳴らしておく
						var sound = Settings.Default.SoundBindings.Where(p => p.IsEnabled && p.Action == action).FirstOrDefault();
						if (sound != null) {
							new SoundPlayer(sound.FileName).Play();
						}
					}
					return;
				#endregion
				default:
                    return;
            }
            // 取得できたか確認する
            if (statuses == null)
            {
                SetStatusMessage(false);
                return;
            }
            if (Settings.Default.EnableUnreadManager)
            {
                Array.ForEach(statuses, item => item.IsNewest = !item.IsAuthor);
            }
            var normalizedStatus = Timelines.Normalize(statuses);
            if (Settings.Default.IgnoreRegex != null)
            {
                normalizedStatus = normalizedStatus.Where(p => !Settings.Default.IgnoreRegex.IsMatch(p.Text)).ToArray();
            }
            if (normalizedStatus.Length > 0)
            {
				//modified by yuki. add TmpOffPopup condition
                if (Settings.Default.EnablePopup && !TmpOffPopup)
                {
                    if (!Settings.Default.PopupOnlyNotActive || this.Invoke<bool>(() => !IsActive))
                    {
                        popupWindow.Show(normalizedStatus);
                    }
                }
                var action = normalizedStatus.Any(p => Regex.IsMatch(p.Text, string.Format(@"{0}[^a-zA-Z_0-9]", Settings.Default.Username))) ? SoundAction.Reply : SoundAction.Status;
                var sound = Settings.Default.SoundBindings.Where(p => p.IsEnabled && p.Action == action).FirstOrDefault();
                if (sound != null)
                {
                    new SoundPlayer(sound.FileName).Play();
                }
            }
            Timelines.Update(statuses);
            // 更新完了
            SetStatusMessage(true);
        }

        private void RefreshTimelineAsync(RefreshTarget target)
        {
            if (!client.IsLogined)
            {
                // ログインしていないので再ログイン
                Login();
                return;
            }
            ThreadPool.QueueUserWorkItem(state => RefreshTimeline((RefreshTarget)state), target);
        }

        private void ForceActivate()
        {
            if (!IsVisible)
            {
                Show();
            }
            Activate();
        }

        private void MainWindow_Initialized(object sender, EventArgs e)
        {
            if (Settings.Default == null)
            {
                return;
            }
            // ウィンドウの位置と状態を復元
            if (Settings.Default.Location.X != 0 || Settings.Default.Location.Y != 0)
            {
                Left = Settings.Default.Location.X;
                Top = Settings.Default.Location.Y;
            }
            if (Settings.Default.Size.Width != 0 || Settings.Default.Size.Height != 0)
            {
                Width = Settings.Default.Size.Width;
                Height = Settings.Default.Size.Height;
            }
            WindowState = Settings.Default.WindowState;
            // Twitter クライアントのイベントを登録
            client.Updated += new EventHandler<UpdateEventArgs>(TwitterClient_Updated);
            client.UpdateFailure += new EventHandler(TwitterClient_UpdateFailure);
            // タイマーを初期化
            refreshTimer.Tick += (_, __) => RefreshTimelineAsync(RefreshTarget.Recent);
            refreshReplyTimer.Tick += (_, __) => RefreshTimelineAsync(RefreshTarget.Replies);
            refreshMessageTimer.Tick += (_, __) => RefreshTimelineAsync(RefreshTarget.Message);
			#region added by yuki.
			if (Properties.Settings.Default.IsEnableSearchTimeline) {
				refreshSearchResultTimer.Tick += (_, __) => RefreshTimelineAsync(RefreshTarget.Search);
			}
			#endregion

			quickSearchTimer.Interval = TimeSpan.FromMilliseconds(250);
            quickSearchTimer.Tick += new EventHandler(QuickSearchTimer_Tick);
            // 通知領域アイコンを初期化
            //if (Environment.OSVersion.Version.Major < 6 || (Environment.OSVersion.Version.Major == 6 && Environment.OSVersion.Version.Minor == 0))
            {
                notifyIcon.Text = App.NAME;
                notifyIcon.Icon = new System.Drawing.Icon(Application.GetResourceStream(new Uri(@".\Resources\MiniTwitter_small.ico", UriKind.Relative)).Stream);
                notifyIcon.MouseClick += new System.Windows.Forms.MouseEventHandler(NotifyIcon_MouseClick);
                notifyIcon.Visible = Settings.Default.EnableNotifyIcon;
            }
            // ポップアップウィンドウのイベントを登録
            popupWindow.CommandBindings.AddRange(new[]
                {
                    new CommandBinding(Commands.Reply, ReplyCommand_Executed),
                    new CommandBinding(Commands.ReTweet, ReTweetCommand_Executed),
                    new CommandBinding(Commands.ReplyMessage, ReplyMessageCommand_Executed),
                    new CommandBinding(Commands.Delete, DeleteCommand_Executed),
                    new CommandBinding(Commands.Favorite, FavoriteCommand_Executed),
                    new CommandBinding(Commands.MoveToStatusPage, MoveToStatusPageCommand_Executed),
                    new CommandBinding(Commands.MoveToUserPage, MoveToUserPageCommand_Executed),
                });
            // タイムラインタブを作成
            InitializeTimeline();
            // プロキシサーバの設定を反映
            InitializeTwitter();
            // ポップアップウィンドウを初期化
            InitializePopupWindow();
            // キーボードショートカットを初期化
            InitializeKeyboardShortcut();
        }

        private void NotifyIcon_MouseClick(object sender, System.Windows.Forms.MouseEventArgs e)
        {
            if (e.Button == System.Windows.Forms.MouseButtons.Left)
            {
                ForceActivate();
            }
            else if (e.Button == System.Windows.Forms.MouseButtons.Right)
            {
                var contextMenu = (ContextMenu)FindResource("notifyMenu");
                contextMenu.Placement = System.Windows.Controls.Primitives.PlacementMode.MousePoint;
                contextMenu.IsOpen = true;
            }
        }

        private void MainWindow_Loaded(object sender, RoutedEventArgs e)
        {
            // Twitter へログイン
            Login();

			#region added by yuki.
			IsShowTwitForm = !Settings.Default.IsHideTwitFormWhenAppStart;
			#endregion
		}

        private void MainWindow_Activated(object sender, EventArgs e)
        {
            TweetTextBox.Focus();
        }

        private void TweetTextBox_KeyDown(object sender, KeyEventArgs e)
        {
            if (Keyboard.Modifiers != ModifierKeys.None)
            {
                return;
            }
            if (popup.IsOpen)
            {
                if (e.Key == Key.Down)
                {
                    var collectionView = CollectionViewSource.GetDefaultView(usersListBox.ItemsSource);
                    collectionView.MoveCurrentToNext();
                }
                else if (e.Key == Key.Up)
                {
                    var collectionView = CollectionViewSource.GetDefaultView(usersListBox.ItemsSource);
                    collectionView.MoveCurrentToPrevious();
                }
            }
            else if (e.Key == Key.Enter)
            {
                var index = TweetTextBox.CaretIndex;
                TweetTextBox.Text = TweetTextBox.Text.Insert(index, Environment.NewLine);
                TweetTextBox.CaretIndex = index + Environment.NewLine.Length;
                e.Handled = true;
            }
        }

        private void TweetTextBox_TextChanged(object sender, TextChangedEventArgs e)
        {
            //var textChange = e.Changes.FirstOrDefault(p => p.AddedLength != 0);
            //if (textChange == null)
            //{
            //    return;
            //}
            //var text = TweetTextBox.Text.Substring(textChange.Offset, textChange.AddedLength);
            //if (text == "@")
            //{
            //    var users = Timelines.TypeAt(TimelineType.Recent).Items.Select(p => p.Sender).Distinct();
            //    if (users.Count() != 0)
            //    {
            //        usersListBox.ItemsSource = users;
            //        popup.PlacementTarget = TweetTextBox;
            //        popup.PlacementRectangle = TweetTextBox.GetRectFromCharacterIndex(textChange.Offset);
            //        popup.IsOpen = true;
            //    }
            //}
        }

        private void MainWindow_Closing(object sender, CancelEventArgs e)
        {
            if (!_isClosing && Settings.Default.EnableNotifyIcon)
            {
                e.Cancel = true;
                Hide();
                return;
            }
            // ウィンドウの設定を保存する
            Settings.Default.Location = RestoreBounds.Location;
            Settings.Default.Size = RestoreBounds.Size;
            Settings.Default.WindowState = WindowState;
            // ユーザータイムラインを保存する
            Settings.Default.Timelines.Clear();
            foreach (var item in Timelines.Where(p => p.Type == TimelineType.User))
            {
                Settings.Default.Timelines.Add(item);
            }
            // その他のウィンドウを破棄
            popupWindow.Close();
            notifyIcon.Visible = false;
        }

        private void TimelineListBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            foreach (ITwitterItem item in e.AddedItems)
            {
                item.IsNewest = false;
            }
        }

        private void TimelineListBox_MouseDoubleClick(object sender, MouseButtonEventArgs e)
        {
            var listBox = (ListBox)sender;
            var item = (ITwitterItem)listBox.SelectedItem;
            if (item == null)
            {
                return;
            }
            var element = (UIElement)listBox.ItemContainerGenerator.ContainerFromItem(item);
            if (element == null || !element.IsMouseOver)
            {
                return;
            }
            TweetTextBox.AppendText((!item.IsMessage ? "@" : "D ") + item.Sender.ScreenName + " ");
            TweetTextBox.CaretIndex = TweetTextBox.Text.Length;
            TweetTextBox.Focus();
        }

        private void HomeButton_Click(object sender, RoutedEventArgs e)
        {
            Process.Start("http://twitter.com/home");
        }

        private void SettingButton_Click(object sender, RoutedEventArgs e)
        {
            SettingDialog dialog = new SettingDialog { Owner = this };
            if (!(dialog.ShowDialog() ?? false))
            {
                return;
            }
            // 正規表現を組みなおす
            Settings.Default.InitializeKeywordRegex();
            // プロキシサーバの設定を反映
            InitializeTwitter();
            // ポップアップウィンドウを初期化
            InitializePopupWindow();
            // キーボードショートカットを初期化
            InitializeKeyboardShortcut();
            // ログインしているか判別
            if (!client.IsLogined)
            {
                // Twitter へログイン
                Login();
                return;
            }
            InitializeAutoRefresh();
            // 通知領域アイコン設定を変更
            notifyIcon.Visible = Settings.Default.EnableNotifyIcon;
            // テーマが変更されているか確認
            Application.Current.ApplyTheme(Settings.Default.Theme);
        }

        private ITwitterItem GetSelectedItem()
        {
            var timeline = (Timeline)TimelineTabControl.SelectedItem;
            return timeline != null ? (ITwitterItem)timeline.View.CurrentItem : null;
        }

        private void UpdateCommand_Executed(object sender, ExecutedRoutedEventArgs e)
        {
            var status = (string)e.Parameter ?? TweetTextBox.Text;
            if (string.IsNullOrEmpty(status))
            {
                return;
            }
            StatusText = "ステータスを更新しています...";
            //client.Update((string)e.Parameter ?? TweetTextBox.Text, in_reply_to_status_id);
            ThreadPool.QueueUserWorkItem(text => client.Update((string)text, in_reply_to_status_id), status);

			#region added by yuki.
			if (Settings.Default.IsCloseTwitFormWhenUpdateStatus) {
				IsShowTwitForm = false;
			}
			#endregion
		}

        private void TwitterClient_Updated(object sender, UpdateEventArgs e)
        {
            var item = e.Item;
            if (!item.IsMessage)
            {
                Timelines.Update(new[] { item });
            }
            else
            {
                Timelines.Update(TimelineType.Message, new[] { item });
            }
            this.Invoke(() =>
            {
                in_reply_to_status_id = null;
                TweetTextBox.Clear();
                StatusText = "ステータスを更新しました";
            });
        }

        private void TwitterClient_UpdateFailure(object sender, EventArgs e)
        {
            this.Invoke(() => StatusText = "更新に失敗しました");
        }

        private void RefreshCommand_Executed(object sender, ExecutedRoutedEventArgs e)
        {
            RefreshTimelineAsync(RefreshTarget.Recent);
        }

        private void ReplyCommand_Executed(object sender, ExecutedRoutedEventArgs e)
        {
            var item = (ITwitterItem)e.Parameter ?? GetSelectedItem();
            if (item is Status)
            {
                TweetTextBox.Text = "@" + item.Sender.ScreenName + " " + TweetTextBox.Text;
                in_reply_to_status_id = item.ID;
            }
            else
            {
                TweetTextBox.Text = "D " + item.Sender.ScreenName + " " + TweetTextBox.Text;
            }
            TweetTextBox.CaretIndex = TweetTextBox.Text.Length;
			#region added by yuki.
			// delete orijinal
			//TweetTextBox.Focus();

			DisplayAndFocusToTwitForm();
			#endregion
            ForceActivate();

        }

        private void ReTweetCommand_Executed(object sender, ExecutedRoutedEventArgs e)
        {
            var item = (Status)e.Parameter ?? GetSelectedItem();
            //ThreadPool.QueueUserWorkItem(id => client.ReTweet((long)id), item.ID);
            TweetTextBox.Text = "RT @" + item.Sender.ScreenName + ": " + item.Text;
            TweetTextBox.CaretIndex = 0;

			#region edited by yuki.
			//TweetTextBox.Focus();
			DisplayAndFocusToTwitForm();
			#endregion

			ForceActivate();
        }

        private void ReplyMessageCommand_Executed(object sender, ExecutedRoutedEventArgs e)
        {
            var item = (ITwitterItem)e.Parameter ?? GetSelectedItem();
            TweetTextBox.Text = "D " + item.Sender.ScreenName + " " + TweetTextBox.Text;
            TweetTextBox.CaretIndex = TweetTextBox.Text.Length;

			#region edited by yuki.
			TweetTextBox.Focus();
			DisplayAndFocusToTwitForm();
			#endregion
			ForceActivate();
        }

        private void DeleteCommand_Executed(object sender, ExecutedRoutedEventArgs e)
        {
            var twitterItem = (ITwitterItem)e.Parameter ?? GetSelectedItem();
            if (twitterItem == null)
            {
                return;
            }
            if (MessageBox.Show("削除してよろしいですか？", App.NAME, MessageBoxButton.YesNo, MessageBoxImage.Question) == MessageBoxResult.No)
            {
                return;
            }
            ThreadPool.QueueUserWorkItem(state =>
                {
                    var item = (ITwitterItem)state;
                    if (!client.Delete(item))
                    {
                        return;
                    }
                    // タイムラインの項目も削除する
                    Timelines.Remove(item);
                }, twitterItem);
        }

        private void FavoriteCommand_Executed(object sender, ExecutedRoutedEventArgs e)
        {
            var twitterItem = (ITwitterItem)e.Parameter ?? GetSelectedItem();
            if (twitterItem == null)
            {
                return;
            }
            ThreadPool.QueueUserWorkItem(state =>
                {
                    var item = (Status)state;
                    if (client.Favorite(item))
                    {
                        // お気に入りを切り替える
                        this.Invoke(() => item.Favorited = !item.Favorited);
                    }
                }, twitterItem);
        }

        private void TimelineStyleCommand_Executed(object sender, ExecutedRoutedEventArgs e)
        {
            Settings.Default.TimelineStyle = (TimelineStyle)e.Parameter;
            Timelines.RefreshAll();
            popupWindow.Timeline.View.Refresh();
        }

        private void MoveToUserPageCommand_Executed(object sender, ExecutedRoutedEventArgs e)
        {
            var item = (ITwitterItem)e.Parameter ?? GetSelectedItem();
            try
            {
                Process.Start("http://twitter.com/" + item.Sender.ScreenName);
            }
            catch
            {
                MessageBox.Show("移動に失敗しました", App.NAME);
            }
        }

        private void MoveToStatusPageCommand_Executed(object sender, ExecutedRoutedEventArgs e)
        {
            var item = (ITwitterItem)e.Parameter ?? GetSelectedItem();
            try
            {
                Process.Start(string.Format("http://twitter.com/{0}/statuses/{1}", item.Sender.ScreenName, item.ID));
            }
            catch
            {
                MessageBox.Show("移動に失敗しました", App.NAME);
            }
        }

        private void MoveToReplyPageCommand_Executed(object sender, ExecutedRoutedEventArgs e)
        {
            var item = (Status)(e.Parameter ?? GetSelectedItem());
            try
            {
                Process.Start(string.Format("http://twitter.com/{0}/statuses/{1}", item.InReplyToScreenName, item.InReplyToStatusID));
            }
            catch
            {
                MessageBox.Show("移動に失敗しました", App.NAME);
            }
        }

        private void ReadAllCommand_Executed(object sender, ExecutedRoutedEventArgs e)
        {
            Timelines.ReadAll();
        }

        private void CopyCommand_Executed(object sender, ExecutedRoutedEventArgs e)
        {
            var text = (string)e.Parameter;
            if (text.IsNullOrEmpty())
            {
                var item = (ITwitterItem)GetSelectedItem();
                if (item == null)
                {
                    return;
                }
                text = item.Text;
            }
            Clipboard.SetText(text);
        }

        private void CopyUrlCommand_Executed(object sender, ExecutedRoutedEventArgs e)
        {
            var item = (ITwitterItem)e.Parameter ?? GetSelectedItem();
            Clipboard.SetText(string.Format("http://twitter.com/{0}/statuses/{1}", item.Sender.ScreenName, item.ID));
        }

        private void SortCategoryCommand_Executed(object sender, ExecutedRoutedEventArgs e)
        {
            var category = (ListSortCategory)e.Parameter;
            if (Settings.Default.SortCategory == category)
            {
                return;
            }
            Settings.Default.SortCategory = category;
            Timelines.Sort(category, Settings.Default.SortDirection);
            popupWindow.Timeline.Sort(category, Settings.Default.SortDirection);
        }

        private void SortDirectionCommand_Executed(object sender, ExecutedRoutedEventArgs e)
        {
            var direction = (ListSortDirection)e.Parameter;
            if (Settings.Default.SortDirection == direction)
            {
                return;
            }
            Settings.Default.SortDirection = direction;
            Timelines.Sort(Settings.Default.SortCategory, direction);
            popupWindow.Timeline.Sort(Settings.Default.SortCategory, direction);
        }

        private void AddTimelineCommand_Executed(object sender, ExecutedRoutedEventArgs e)
        {
            var dialog = new TimelineDialog();
            if (!(dialog.ShowDialog() ?? false))
            {
                return;
            }
            var timeline = dialog.Timeline;
            timeline.Update(Timelines.TypeAt(TimelineType.Recent).Items);
            timeline.Update(Timelines.TypeAt(TimelineType.Replies).Items);
            timeline.Sort(Settings.Default.SortCategory, Settings.Default.SortDirection);
            Timelines.Add(timeline);
        }

        private void EditTimelineCommand_Executed(object sender, ExecutedRoutedEventArgs e)
        {
            var timeline = (Timeline)TimelineTabControl.SelectedItem;
            var dialog = new TimelineDialog { Timeline = timeline };
            if (!(dialog.ShowDialog() ?? false))
            {
                return;
            }
            timeline.Clear();
            timeline.Update(Timelines.TypeAt(TimelineType.Recent).Items);
            timeline.Update(Timelines.TypeAt(TimelineType.Replies).Items);
        }

        private void DeleteTimelineCommand_Executed(object sender, ExecutedRoutedEventArgs e)
        {
            var timeline = (Timeline)TimelineTabControl.SelectedItem;
            Timelines.Remove(timeline);
        }

        private void ClearTimelineCommand_Executed(object sender, ExecutedRoutedEventArgs e)
        {
            var timeline = (Timeline)TimelineTabControl.SelectedItem;
            timeline.Clear();
        }

        private void PasteCommand_Executed(object sender, ExecutedRoutedEventArgs e)
        {
            if (Clipboard.ContainsText())
            {
                TweetTextBox.Text = TweetTextBox.Text.Insert(TweetTextBox.CaretIndex, Clipboard.GetText());
                TweetTextBox.CaretIndex = TweetTextBox.Text.Length;
            }
        }

        private void QuickSearchTimer_Tick(object sender, EventArgs e)
        {
            QuickSearch();
        }

        private void QuickSearch()
        {
            Timelines.SearchAll(searchTermTextBox.Text);
            quickSearchTimer.Stop();
        }

        private void SearchTermTextBox_KeyUp(object sender, KeyEventArgs e)
        {
            quickSearchTimer.Stop();
            quickSearchTimer.Start();
        }

        private void SearchCancelButton_Click(object sender, RoutedEventArgs e)
        {
            searchTermTextBox.Clear();
            QuickSearch();
        }

        private void CloseMenuItem_Click(object sender, RoutedEventArgs e)
        {
            _isClosing = true;
            Close();
		}

		#region edited by yuki.
		private void TmpOffPopupCommand_Executed(object sender, ExecutedRoutedEventArgs e) {
		    TmpOffPopup = !TmpOffPopup;
		}

		private void SwitchTwitForm_Executed(object sender, ExecutedRoutedEventArgs e) {
			IsShowTwitForm = !IsShowTwitForm;
			this.TweetTextBox.Focus();
		}

		private void DisplayAndFocusToTwitForm()  {
			if (!IsShowTwitForm) {
				IsShowTwitForm = true;
				this.TweetTextBox.Focus();
			}
		}

		private void DisplayTwitForm_Executed(object sender, ExecutedRoutedEventArgs e) {
			DisplayAndFocusToTwitForm();
		}

		private void LineUp_Executed(object sender, ExecutedRoutedEventArgs e) {
			var listbox = (ListBox)sender;
			var scrollViewer = GetScrollViewer(listbox) as ScrollViewer;
			if (scrollViewer != null) {
				scrollViewer.LineUp();
			}
		}

		private void LineDown_Executed(object sender, ExecutedRoutedEventArgs e) {
			var listbox = (ListBox)sender;
			var scrollViewer = GetScrollViewer(listbox) as ScrollViewer;
			if (scrollViewer != null) {
				scrollViewer.LineDown();
			}
		}

		public static DependencyObject GetScrollViewer(DependencyObject o) {
			if (o is ScrollViewer) { return o; }

			for (int i = 0; i < VisualTreeHelper.GetChildrenCount(o); i++) {
				var child = VisualTreeHelper.GetChild(o, i);

				var result = GetScrollViewer(child);
				if (result == null) {
					continue;
				} else {
					return result;
				}
			}
			return null;
		}
		#endregion

		#region INotifyPropertyChanged メンバ

		public event PropertyChangedEventHandler PropertyChanged;

		#endregion

		public void OnPropertyChanged(string propertyName) {
			var handler = PropertyChanged;
			if (handler != null) {
				handler(this, new PropertyChangedEventArgs(propertyName));
			}
		}

	}
}